Skip to content

fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持(修 QQ 切微软拼音崩溃)#287

Merged
appergb merged 2 commits into
mainfrom
fix/windows-ime-static-crt
May 6, 2026
Merged

fix(windows-ime): 静态链接 CRT 防 host 进程 MSVCP140 劫持(修 QQ 切微软拼音崩溃)#287
appergb merged 2 commits into
mainfrom
fix/windows-ime-static-crt

Conversation

@appergb
Copy link
Copy Markdown
Collaborator

@appergb appergb commented May 6, 2026

User description

用户反馈

Windows 11 上 QQ (QQEX.exe v9.9.26.44725) 切到微软拼音时崩溃:

```
异常代码:0xc0000005 (STATUS_ACCESS_VIOLATION)
故障模块:D:\program\QQ\versions\\resources\app\MSVCP140.dll
偏移:0x13080
```

根因(DLL Hell)

  1. QQ 把私有 MSVCP140.dll 放在 app/ 目录,DLL 搜索顺序优先于系统
  2. ctfmon 切微软拼音 → 把已注册 TSF DLL 全部加载进 QQ 进程
  3. 我们 `OpenLessIme.dll` 用 MSBuild 默认 `RuntimeLibrary=MultiThreadedDLL` —— 动态链接 MSVCP140
  4. 在 QQ 进程内被绑给 QQ 自带的旧版 MSVCP140 → 调 `std::wstring/thread/mutex` 跑的是 QQ 那份 → ABI 漂移 → AV

修复(surgical)

`OpenLessIme.vcxproj` 4 处 `ItemDefinitionGroup` 加 `MultiThreaded(Debug)`:

  • DLL 自带 MSVCP / VCRUNTIME / UCRT 副本
  • QQ 私有 CRT 劫持不到我们
  • DLL 体积 +~300 KB(可接受)
  • DLL 内部 STL 不能跨边界共享 —— 我们 IPC 走 named pipe + JSON,不受影响

不动的部分(已合规)

  • TSF 接口本身已是 COM C ABI(vtable + COM-typed 参数)
  • 重量级 ASR/LLM/polish 早已在 OpenLess.exe 主进程做,DLL 只做 TSF text service hooks + IPC

QQ 客观脆弱性(已整理详细报告,用于 TSRC 上报)

  • app/ 目录 MSVCP140.dll 劫持系统 CRT
  • 版本目录嵌套混乱(崩溃报告里 9.9.26.44725 vs 路径里 9.9.29-47354)
  • 无 IME 崩溃隔离机制(任何第三方 IME 崩 = QQ 全进程崩)
  • 自带 CRT 与系统 CRT 行为漂移

Test plan

  • CI `Windows Tauri checks` + `pr_agent_job`
  • 真机 Windows:QQ 切微软拼音不再崩
  • OpenLessIme.dll 体积 +~300 KB(确认在可接受范围)

PR Type

Bug fix, Enhancement


Description

  • 静态链接 OpenLessIme.dll 的 CRT

  • 避免宿主进程 DLL 劫持崩溃

  • 保留线程库通知用于 CRT 初始化


Diagram Walkthrough

flowchart LR
  A["QQ / Office 宿主进程"] -- "加载 TSF DLL" --> B["OpenLessIme.dll"]
  B -- "改为 /MT 静态 CRT" --> C["自带 MSVCP/VCRUNTIME/UCRT"]
  A -- "app/ 目录私有 DLL 劫持" --> D["旧版 MSVCP140.dll"]
  C -- "不再依赖宿主 CRT" --> E["避免 ABI 漂移与崩溃"]
  B -- "保留线程通知" --> F["CRT 线程本地初始化/清理"]
Loading

File Walkthrough

Relevant files
Bug fix
dllmain.cpp
Preserve thread notifications for static CRT                         

openless-all/app/windows-ime/src/dllmain.cpp

  • Removed DisableThreadLibraryCalls during process attach
  • Added comment explaining static CRT thread attach/detach needs
  • Prevents TLS initialization and cleanup issues in host input threads
+5/-1     
Configuration changes
OpenLessIme.vcxproj
Switch DLL build to static CRT                                                     

openless-all/app/windows-ime/OpenLessIme.vcxproj

  • Set RuntimeLibrary to MultiThreadedDebug for Debug builds
  • Set RuntimeLibrary to MultiThreaded for Release builds
  • Applied changes for both Win32 and x64 configurations
  • Documented why static CRT avoids host-side MSVCP140.dll hijacking
+9/-0     

用户反馈:在 Windows 11 切到微软拼音时 QQ (QQEX.exe v9.9.26.44725) 崩
0xc0000005,故障模块 D:\program\QQ\versions\<ver>\resources\app\
MSVCP140.dll @ 偏移 0x13080。

根因(DLL Hell 经典场景):
1. QQ 把私有版本的 MSVCP140.dll 放在 app/ 目录,DLL 搜索顺序优先于系统
2. ctfmon 切微软拼音 → 把所有已注册 TSF DLL 加载进 QQ 进程
3. 我们 OpenLessIme.dll 此前用 MSBuild 默认 RuntimeLibrary(=
   MultiThreadedDLL,动态链接 MSVCP140.dll/VCRUNTIME140.dll/UCRT)
4. 在 QQ 进程内被绑给 QQ 自带的旧版 MSVCP140 → 调 std::wstring /
   std::thread / std::mutex 等跑的是 QQ 那份 → ABI 行为漂移 → AV

修复:
- vcxproj 4 处 ItemDefinitionGroup(Debug/Release × Win32/x64)显式声明
  <RuntimeLibrary>MultiThreaded</RuntimeLibrary>(Release)/
  <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>(Debug)。
- DLL 自带 CRT 副本,QQ 私有 MSVCP140 劫持不到。
- DLL 体积 +~300 KB;DLL 内部 STL 不能跨边界共享,但我们 IPC 走 named
  pipe + JSON(C ABI 友好),不受影响。

不动的部分:
- TSF 接口本身已是 COM C ABI(vtable + COM-typed 参数),无需改
- 重量级 ASR/LLM/polish 早已在 OpenLess.exe 主进程,DLL 只做 TSF text
  service hooks + IPC(已合规"组件极简,IPC 通信"原则)

QQ 客观脆弱性已整理详细报告(桌面 PDF/DOCX + GitHub issue),用于上报
TSRC:app/ 目录下 MSVCP140 劫持系统 CRT、版本号目录嵌套混乱、无 IME
崩溃隔离机制、CRT 漂移。
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80de07851c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OPENLESSIME_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove DisableThreadLibraryCalls when using /MT

With the Release builds now linked to the static CRT (MultiThreaded), this DLL still calls DisableThreadLibraryCalls(instance) in windows-ime/src/dllmain.cpp; the Win32 API documentation explicitly says not to call DisableThreadLibraryCalls from a DLL linked to the static CRT because the CRT needs thread attach/detach notifications. In host processes that create threads after loading the TSF DLL, suppressing those callbacks can break CRT per-thread initialization/cleanup and reintroduce crashes or leaks in exactly the arbitrary-host-process scenario this change is meant to harden, so remove that call when using /MT (same issue applies to the Win32 Release setting).

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

PR Reviewer Guide 🔍

(Review updated until commit 901e9df)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

Codex P1 (PR #287):DLL 改用 /MT 静态链接 CRT 后,仍调
DisableThreadLibraryCalls(instance) 会让静态 CRT 拿不到 DLL_THREAD_DETACH
通知,per-thread TLS 初始化/清理断裂 — 在 host 进程(QQ / Office /
微软拼音切换)创建新 input thread 时反而把这次想修的崩溃问题重新引回。

Microsoft 文档 DisableThreadLibraryCalls 备注明确:使用 static CRT 的
DLL 不应禁用 thread library calls。

修复:删除该调用;保留 g_module 缓存。
@appergb
Copy link
Copy Markdown
Collaborator Author

appergb commented May 6, 2026

@codex P1 已采纳:commit 901e9df 删除了 DisableThreadLibraryCalls(instance)。理由完全同意——/MT 下静态 CRT 需要 DLL_THREAD_DETACH 通知做 per-thread 资源清理,禁用通知会让 thread-local 在 host 进程的 input thread 切换时泄漏,反而引入这个 PR 想修的同类崩溃。

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Persistent review updated to latest commit 901e9df

@appergb appergb merged commit 96ca966 into main May 6, 2026
2 checks passed
@appergb appergb deleted the fix/windows-ime-static-crt branch May 6, 2026 11:52
appergb pushed a commit that referenced this pull request May 6, 2026
主要修复
─────────
- fix(windows): TSF DLL 静态链接 CRT,防 host 进程(QQ 等)私有
  MSVCP140 劫持导致切微软拼音崩溃 0xc0000005(PR #287 + #289 + Codex
  P1 review)。
- fix(windows): 移除 DisableThreadLibraryCalls — /MT 下 CRT 需要
  thread attach/detach 通知做 per-thread TLS 清理,禁用反而触发崩溃。
- fix(recording): 录音条出现时 mic 已 capture,按 Option 不再吞开头
  字。emit Recording 加 race 检查避免短按时覆盖 stop/cancel UI 信号。
- fix(startup): splash 透明背景 + 卡片化,避免长启动时左半白屏。
- fix(vault): keyring chunks 用稳定 account name —— macOS Keychain
  Always-Allow 不再因 UUID 轮换失效;不再每次 load 都尝试删 9 个
  legacy entries 触发 ACL 弹窗(与 PR #277 一起)。

新功能
─────────
- feat(security): 凭据从 plaintext JSON 迁移到平台 credential vault
  (macOS Keychain / Windows Credential Manager / Linux libsecret)。
  Windows 2560 byte 限制下用 chunked storage;partial-write safe。
  legacy 多源迁移完成后自动清理(PR #277)。

工程
─────────
- chore(ci): 暂时搁置 macos-13(Intel mac)matrix —— GH Actions
  runner pool 紧张到每次 dispatch queue 1-2h(已观测 4 次)。Apple
  Silicon 用户 dmg 仍发;Intel mac 用户保留 v1.2.20 dmg + Rosetta
  说明(issue #299)。
- chore(devex): CI 加 macOS 矩阵 + 5-way 版本号一致性校验 +
  scripts/bump-version.sh 一行同步 5 处版本号。.gitignore 加
  promo-openless-v2 / node_modules / dist / target 全局兜底(PR #294)。
- docs: docs/audit-2026-05-06.md 工程审计基线,覆盖架构 / UI bug /
  多端一致性 / IPC 1:1 校验 / P0/P1/P2 改进路径。
- chore: 9 个改进 issue (#295-#303) 跟踪 audit 列出的所有 P1/P2/P3
  问题。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant